package com.xbongbong.rule.engine;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.jeasy.rules.api.*;
import org.jeasy.rules.core.DefaultRulesEngine;
import org.springframework.util.CollectionUtils;
import com.xbongbong.rule.rules.XbbRule;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 构造规则引擎
 * <i>使用参考示例：</i>
 * <pre class="code">
 *       XbbRuleEngine&lt;JSONObject&gt; xbbRuleEngine = new XbbRuleEngine<>(rules);
 *       // 构造引擎
 *       xbbRuleEngine.build();
 *       xbbRuleEngine.fire(fact);
 *     </pre>
 * @author zheng.li
 * @param <T>
 */
@Slf4j
public class XbbRuleEngine<T> extends RulesEngineParameters {

    /**
     * 规则集合
     */
    private List<XbbRule<T>> xbbRules;

    /**
     * 自定义的规则监听器
     *
     * <i>
     *     当规则集合中的执行逻辑相同时，也可通过自定义监听器的方式，定义共同的处理逻辑
     * </i>
     * <p>
     *     优先级低于默认的监听器RuleSuccessListener
     * </p>
     */
    private List<RuleListener> listeners;

    /**
     * 匹配到的规则,用于返回
     */
    private List<XbbRule<T>> matchedXbbRules = new ArrayList<>();

    /**
     * 规则id - 规则 映射
     */
    private Map<String,XbbRule<T>> rulesMap;

    /**
     * easy-rules 规则引擎
     */
    private DefaultRulesEngine rulesEngine;

    /**
     * 注册的 easy-rules 规则
     */
    private Rules rules;

    /**
     * 待校验的数据
     */
    T xbbFact;



    public XbbRuleEngine() {

    }

    public XbbRuleEngine(List<XbbRule<T>> xbbRules) {
        this.xbbRules = xbbRules;
    }

    /**
     * 设置规则集合
     * @param xbbRules List<XbbRule<T>>
     */
    public void setXbbRules(List<XbbRule<T>> xbbRules) {
        this.xbbRules = xbbRules;
    }

    /**
     * 获取规则集合
     * @return List<XbbRule<T>>
     */
    public List<XbbRule<T>> getXbbRules() {
        return xbbRules;
    }

    /**
     * 设置自定义监听器
     * @param listeners List<RuleListener>
     */
    public void setListeners(List<RuleListener> listeners) {
        this.listeners = listeners;
    }

    /**
     * 获取自定义的监听器
     * @return List<RuleListener>
     */
    public List<RuleListener> getListeners() {
        return listeners;
    }

    public T getXbbFact() {
        return xbbFact;
    }

    public void setXbbFact(T xbbFact) {
        this.xbbFact = xbbFact;
    }

    /**
     * 规则匹配监听器，用于匹配成功后，触发规则
     */
    public class RuleSuccessListener implements RuleListener {

        @SneakyThrows
        @Override
        @SuppressWarnings("unchecked")
        public void onSuccess(Rule rule, Facts facts) {
            XbbRule<T> xbbRule = getRulesMap().get(rule.getName());
            // 保存匹配到的规则
            matchedXbbRules.add(xbbRule);
            // 触发规则
            if (xbbRule.getXbbAction() != null) {
                log.info("规则触发!");
                T fact = (T) facts.iterator().next().getValue();
                xbbRule.getXbbAction().action(fact);
            }
        }
    }

    /**
     * 构建规则引擎
     * @return DefaultRulesEngine
     */
    public DefaultRulesEngine build() {
        // 构建规则引擎参数
        RulesEngineParameters parameters = new RulesEngineParameters();
        // 找出所有匹配规则（false），还是匹配一个即结束（true）
        parameters.setSkipOnFirstAppliedRule(isSkipOnFirstAppliedRule());
        // 当有一个规则触发时出现异常，则结束流程
        parameters.setSkipOnFirstFailedRule(isSkipOnFirstFailedRule());
        // 当有一个规则未匹配时，则结束流程
        parameters.setSkipOnFirstNonTriggeredRule(isSkipOnFirstNonTriggeredRule());
        // 如果优先级超过用户定义的阈值，则跳过下一个规则的参数。
        parameters.setPriorityThreshold(getPriorityThreshold());
        rulesEngine = new DefaultRulesEngine(parameters);
        // 注册默认的监听器
        rulesEngine.registerRuleListener(new RuleSuccessListener());
        if (!CollectionUtils.isEmpty(listeners)) {
            rulesEngine.registerRuleListeners(listeners);
        }
        // 构建注册规则
        rules = new Rules();
        for (XbbRule<T> xbbRule : xbbRules) {
            rules.register(xbbRule.toEasyRule());
        }

        return rulesEngine;
    }

    /**
     * 根据给定的事实开始匹配规则
     * @return 匹配的规则
     */
    public List<XbbRule<T>> fire(T xbbFact) {

        if (rulesEngine == null) {
            rulesEngine = build();
        }
        // 清空上次结果
        matchedXbbRules = new ArrayList<>();
        // 转换为Fact
        Facts thisFacts = new Facts();
        thisFacts.put(getFactName(xbbFact), xbbFact);
        // 根据给定的事实触发所有已注册的规则。
        rulesEngine.fire(rules, thisFacts);
        // 返回匹配的规则
        return matchedXbbRules;
    }

    public List<XbbRule<T>> fire(){
        return fire(this.xbbFact);
    }

    /**
     * 获取rulesMap
     * @return Map<String,XbbRule<T>>
     */
    private Map<String, XbbRule<T>> getRulesMap() {
        if (this.rulesMap != null) {
            return rulesMap;
        }
        rulesMap = new HashMap<>(16);
        for (XbbRule<T> xbbRule : this.xbbRules) {
            rulesMap.put(xbbRule.getRuleId(), xbbRule);
        }
        return rulesMap;
    }

    /**
     * 获取fact实体的name，以类名做为name
     * @return factName
     */
    private String getFactName(T xbbFact) {
        return xbbFact.getClass().getSimpleName();
    }


}
